package ru.spb.osll.myfit.client.widget;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import ru.spb.osll.myfit.client.Mode;
import ru.spb.osll.myfit.common.PageElement;
import ru.spb.osll.myfit.common.PageElementType;
import ru.spb.osll.myfit.common.Table;
import ru.spb.osll.myfit.common.TableCell;
import ru.spb.osll.myfit.common.TableStyle;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.regexp.shared.MatchResult;
import com.google.gwt.regexp.shared.RegExp;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.MenuItem;
import com.google.gwt.user.client.ui.TextBox;

public abstract class TableWidget extends PageElementWidget {

  protected int m_selectedRow = -1;
  protected MyFlexTable m_flexTable;// = new FlexTable();
  protected int m_currentCell;
  private TableStyle m_tableStyle;

  protected MenuItem m_deleteRowItem;
  protected MenuItem m_addRowItem;
  protected MenuItem m_deleteCellItem;
  protected MenuItem m_addCellItem;
  private Map<String, String> m_variables = new HashMap<String, String>();
  protected boolean hasVerificationError = false;
  private String regExp = "((\\$\\{)(.+?)(\\}))";
  private boolean isUndecorated;
  
  protected Command deleteRowCommand = new Command() {
    public void execute() {
      m_rootPopupMenu.hide();
      if (m_selectedRow >= 0) {
        m_flexTable.removeRow(m_selectedRow);
        updateWidth();
      }
      if (m_flexTable.getRowCount() == 0) {
        removeFromParent();
        try {
          this.finalize();
        } catch (Throwable e) {
          // TODO Auto-generated catch block
          // e.printStackTrace();
        }
      }
    }
  };

  protected Command addRowCommand = new Command() {
    public void execute() {
      m_rootPopupMenu.hide();
      if (m_selectedRow >= 0)
        addNewRow(m_selectedRow + 1);
    }
  };

  protected Command deleteCellCommand = new Command() {
    public void execute() {
      m_rootPopupMenu.hide();
      if (m_selectedRow >= 0 && m_currentCell >= 0) {
        deleteCell(m_selectedRow, m_currentCell);
      }
    }
  };

  protected Command addCellCommand = new Command() {
    public void execute() {
      m_rootPopupMenu.hide();
      if (m_selectedRow > 0 && m_currentCell >= 0) {
        addNewCell(m_selectedRow, m_currentCell + 1);
      }
    }
  };
  
  protected Command commentTable = new Command() {
    public void execute() {
      m_rootPopupMenu.hide();
      commentTable();
    }
  };
  
  protected Command uncommentTable = new Command() {
    public void execute() {
      m_rootPopupMenu.hide();
      uncommentTable();
    }
  };
  
  protected void commentTable(){
    m_flexTable.insertRow(0);
    TextBox tb = createTextEdit(new TableCell("Comment")); 
    m_flexTable.setWidget(0, 0, tb);
    tb.setReadOnly(true);
    updateWidth();
  }
  
  protected void uncommentTable(){
    if(m_flexTable.getCellCount(0)== 1){
      TextBox tb = (TextBox) m_flexTable.getWidget(0, 0);
      if(tb.getText() == "Comment"){
        m_flexTable.removeRow(0);
      }
    }
  } 
  

  public TableWidget(GWTWidgetFactory gwtWidgetFactory, Table table) {
    super(gwtWidgetFactory);
    hasVerificationError = table.hasVerificationError();
    isUndecorated = table.isUndecorated();
    sinkEvents(Event.ONMOUSEUP | Event.ONDBLCLICK | Event.ONCONTEXTMENU);
    createPopupMenu();
    addCustomItemInMenu();
    m_flexTable = m_gwtWidgetFactory.createMyFlexTable();
    m_flexTable.setCellSpacing(0);
    m_flexTable.setCellPadding(0);
    m_flexTable.addStyleName(IStyleConstants.FLEX_TABLE_STYLE_NAME);
    insertTable(table);
    updateWidth();
    initWidget(m_flexTable);
  }

  protected void insertTable(Table table) {
    setTableStyle(table.getStyle());
    Vector<Vector<TableCell>> data = table.getData();
    int rowSize = data.size();
    for (int i = 0; i < rowSize; ++i) {
      List<TableCell> cellLine = data.elementAt(i);
      addRow(i, cellLine);
    }
  }

  protected void addRow(int row, List<TableCell> data) {

    int cellCount = data.size();
    for (int j = 0; j < cellCount; ++j) {
      TableCell tableCell = data.get(j);
      TextBox tb = createTextEdit(tableCell);
      m_flexTable.setWidget(row, j, tb);
      setStyles(tableCell, row, j);
    }
  }

  private void setStyles(TableCell tableCell, int row, int cell) {
    switch (tableCell.getTestResult()) {
    case PASS:
      m_flexTable.getCellFormatter().addStyleName(row, cell,
          IStyleConstants.TABLE_CELL_PASS_STYLE_NAME);
      break;
    case EXCEPTION:
      m_flexTable.getCellFormatter().addStyleName(row, cell,
          IStyleConstants.TABLE_CELL_EXCEPTION_STYLE_NAME);
      TextBox tb = (TextBox) m_flexTable.getWidget(row, cell);
      Tooltip tooltip = new Tooltip(tableCell.getTooltip());
      tb.addMouseListener(new TooltipMouseListener(tooltip));
      break;
    case FAIL:
      m_flexTable.getCellFormatter().addStyleName(row, cell,
          IStyleConstants.TABLE_CELL_FAIL_STYLE_NAME);
      break;
    default:
      break;
    }
    setSpecifiedStyles(row, cell);
  }

  abstract protected void setSpecifiedStyles(int row, int cell);

  abstract protected void addNewRow(int row);

  abstract protected void addNewCell(int row, int cell);

  abstract protected void deleteCell(int row, int cell);

  abstract protected void refreshTable(FlexTable table);

  abstract protected void setReadOnly(boolean isReadOnly);

  abstract protected void tuneMenu();

  public void parseVariables(HashMap<String, String> variables) {
    m_variables = variables;
    updateCells();

  }

  protected void updateCells() {
    int rowCount = m_flexTable.getRowCount();
    for (int i = 0; i < rowCount; i++) {
      int cellCount = m_flexTable.getCellCount(i);
      for (int j = 0; j < cellCount; ++j) {
        try {
          updateTextBox(((TextBox) m_flexTable.getWidget(i, j)));
        } catch (Exception ex) {
        }
      }
    }
  }

  protected void updateTextBox(TextBox tb) {
    String text = ((CellTextBox) tb).getRawText().trim();
    tb.removeStyleName(IStyleConstants.CELL_TEXT_BOX_UNKNOWN_VARIABLE_STYLE_NAME);
    String parsedText = parseText(text);
    ((CellTextBox) tb).setParsedText(parsedText);

    if (isVariable(text) && !variablesIsOk(text)) {
      tb.addStyleName(IStyleConstants.CELL_TEXT_BOX_UNKNOWN_VARIABLE_STYLE_NAME);
    }
  }

  private void addCustomItemInMenu() {
    m_rootMenuBar.addSeparator();

    m_deleteRowItem = m_gwtWidgetFactory.createMenuItem("Delete row", true,
        deleteRowCommand);
    m_addRowItem = m_gwtWidgetFactory.createMenuItem("Add row", true,
        addRowCommand);
    m_addCellItem = m_gwtWidgetFactory.createMenuItem("Add cell", true,
        addCellCommand);
    m_deleteCellItem = m_gwtWidgetFactory.createMenuItem("Delete cell", true,
        deleteCellCommand);
    
    MenuItem commentTableItem = m_gwtWidgetFactory.createMenuItem("Comment table", true,
        commentTable);
    
    MenuItem uncommentTableItem = m_gwtWidgetFactory.createMenuItem("Uncomment table", true,
        uncommentTable);

    m_rootMenuBar.addItem(m_deleteRowItem);
    m_rootMenuBar.addItem(m_addRowItem);
    m_rootMenuBar.addItem(m_deleteCellItem);
    m_rootMenuBar.addItem(m_addCellItem);
    m_rootMenuBar.addSeparator();
    m_rootMenuBar.addItem(commentTableItem);
    m_rootMenuBar.addItem(uncommentTableItem);
    
  }

  private boolean isVariable(String text) {
    return (RegExp.compile(regExp).exec(text.trim()) != null);
  }

  private String getVariableName(String text) {
    RegExp varRegExp = RegExp.compile(regExp);
    MatchResult m = varRegExp.exec(text);
    if (m != null) {
      return m.getGroup(3);
    } else {
      return " ";
    }
  }

  private int getVariableLength(String text) {
    RegExp varRegExp = RegExp.compile(regExp);
    MatchResult m = varRegExp.exec(text);
    if (m != null) {
      return m.getGroup(1).length();
    } else {
      return -1;
    }
  }

  private int getVariablePos(String text) {
    RegExp varRegExp = RegExp.compile(regExp);
    MatchResult m = varRegExp.exec(text);
    if (m != null)
      return m.getIndex();
    else
      return -1;
  }

  private boolean variablesIsOk(String text) {
    String str = text;
    while (isVariable(str)) {
      String name = getVariableName(str);
      if (!m_variables.containsKey(name))
        return false;
      str = str.substring(getVariablePos(str) + getVariableLength(str));
    }

    return true;
  }

  private String parseText(String text) {
    RegExp varRegExp = RegExp.compile(regExp, "g");
    MatchResult m;
    String newText = text;
    String value;
    while ((m = varRegExp.exec(text)) != null) {
      value = m_variables.get(m.getGroup(3));
      if (value != null)
        newText = newText.replace(m.getGroup(0), value);
    }
    return newText;

  }

  protected TextBox createTextEdit(TableCell cell) {
    String text = cell.getText().trim();
    CellTextBox tb = new CellTextBox(text, text);
    tb.setStyleName(IStyleConstants.CELL_TEXT_BOX_STYLE_NAME);

    if (hasVerificationError) {
      tb.addStyleName(IStyleConstants.CELL_TEXT_BOX_VERIFICATION_ERROR_STYLE_NAME);
    }

    tb.addKeyPressHandler(new KeyPressHandler() {
      @Override
      public void onKeyPress(KeyPressEvent event) {
        TextBox tb = (TextBox) event.getSource();
        if (!tb.isReadOnly()) {
          // if (event.getCharCode() == KeyCodes.KEY_ESCAPE) {
          // HorizontalPanel parent = (HorizontalPanel) tb.getParent();
          // tb.removeFromParent();
          // if (parent.getWidgetCount() == 0)
          // refreshTable((FlexTable) parent.getParent());
          // } else
          if (event.getCharCode() == KeyCodes.KEY_BACKSPACE ||
              event.getCharCode() == KeyCodes.KEY_DELETE) {
            changeWidth((TextBox) event.getSource(), -1);
          } else if (event.getCharCode() > 47 || event.getCharCode() == 32) {
            changeWidth(((TextBox) event.getSource()), 1);
          }
        }
      }
    });

    tb.addChangeHandler(new ChangeHandler() {

      @Override
      public void onChange(ChangeEvent changeEvent) {
        updateTextBox((CellTextBox) changeEvent.getSource());

      }
    });

    tb.addClickHandler(new ClickHandler() {
      public void onClick(ClickEvent event) {
        m_currentCell = m_flexTable.getCellForEvent(event).getCellIndex();
        m_selectedRow = m_flexTable.getCellForEvent(event).getRowIndex();
      }
    });

    tb.addKeyUpHandler(new KeyUpHandler() {
      @Override
      public void onKeyUp(KeyUpEvent event) {
        changeWidth(((TextBox) event.getSource()), 0);
      }
    });

    return tb;
  }

  protected void changeWidth(TextBox tb, int displace) {
    int length = tb.getText().length() + displace;
    tb.setVisibleLength(length);
  }

  @Override
  protected void onRightClick(Event event) {
    super.onRightClick(event);
    if (m_mode.equals(Mode.VIEW)) {
      return;
    }
    Element td = m_flexTable.getEventTargetCell(event);
    if (td == null) {
      m_selectedRow = -1;
    }
    m_currentCell = td.getChildCount();
    Element tr = DOM.getParent(td);
    Element body = DOM.getParent(tr);
    m_selectedRow = DOM.getChildIndex(body, tr);
    m_currentCell = DOM.getChildIndex(tr, td);
    GWT.log("selectedRow " + m_selectedRow);
    tuneMenu();
  }

  @Override
  protected void onLeftClick(Event event) {
    Element td = m_flexTable.getEventTargetCell(event);
    if (td == null) {
      m_selectedRow = -1;
      return;
    }
    m_currentCell = td.getChildCount();
    Element tr = DOM.getParent(td);
    Element body = DOM.getParent(tr);
    m_selectedRow = DOM.getChildIndex(body, tr);
    m_currentCell = DOM.getChildIndex(tr, td);
    TextBox tb = (TextBox) m_flexTable.getWidget(m_selectedRow, m_currentCell);
    tb.getElement().focus();
    // GWT.log("selectedRow " + m_selectedRow);
    // tuneMenu();
  }

  /**
   * Disable / enable cells of the widgets
   * 
   * @param mode
   */
  @Override
  public void setMode(Mode mode) {
    boolean isReadOnly = true;
    switch (mode) {
    case EDIT:
      this.m_mode = Mode.EDIT;
      isReadOnly = false;
      break;
    case VIEW:
      this.m_mode = Mode.VIEW;
      isReadOnly = true;
      break;
    }
    setReadOnly(isReadOnly);
  }

  public void setTableStyle(TableStyle tableStyle) {
    m_tableStyle = tableStyle;
  }

  public TableStyle getTableStyle() {
    return m_tableStyle;
  }

  @Override
  public void setOpen(boolean isOpen) {
    // no action needed.
  }

  @Override
  public PageElementType getType() {
    return PageElementType.TABLE;

  }

  @Override
  public PageElement getPageElement() {
    Table table = new Table(getTableStyle());
    int rowCount = m_flexTable.getRowCount();
    for (int i = 0; i < rowCount; ++i) {
      Vector<String> line = new Vector<String>();
      int cellCount = m_flexTable.getCellCount(i);
      for (int j = 0; j < cellCount; ++j)
        try {
          String text = ((CellTextBox) m_flexTable.getWidget(i, j))
              .getRawText();
          text = (text.isEmpty()) ? "" : text;
          line.addElement(text);
        } catch (Exception ex) {
        }
      if (!line.isEmpty()) {
        Vector<TableCell> lineCell = new Vector<TableCell>();
        for (String item : line) {
          TableCell tableCell = new TableCell();
          tableCell.setText(item);
          lineCell.add(tableCell);
        }
        table.addLine(lineCell);
      }
    }
    table.setUndecorated(isUndecorated);
    return table;
  }

  protected void updateWidth() {
    int rowCount = m_flexTable.getRowCount();
    int maxCellCount = 0;
    for (int i = 0; i < rowCount; i++) {
      int cellcount = m_flexTable.getCellCount(i);
      maxCellCount = maxCellCount > cellcount ? maxCellCount : cellcount;
      for (int j = 0; j < cellcount; j++) {
        m_flexTable.getFlexCellFormatter().setColSpan(i, j, 0);
      }
    }

    for (int col = 1; col <= maxCellCount; col++) {
      for (int i = 0; i < rowCount; i++) {
        int cellcount = m_flexTable.getCellCount(i);
        if (cellcount == col) {
          m_flexTable.getFlexCellFormatter().setColSpan(i, col - 1,
              maxCellCount - col + 1);
        }
      }
    }
  }
}
